package lk.icta.common.service.impl;

import lk.icta.common.entity.DSDivision;
import lk.icta.common.entity.GNDivision;
import lk.icta.common.service.MailSenderService;
import lk.icta.user.entity.User;
import lk.icta.user.service.UserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.task.TaskExecutor;
import org.springframework.mail.MailSendException;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;


/**
 * @author Kusal Samarakoon
 * @author Supun Viraj Rathnayaka
 */

public class MailSenderServiceImpl implements MailSenderService {


    private static final Logger logger = LoggerFactory.getLogger(MailSenderServiceImpl.class);

    private MailSender mailSender;
    private final UserManager userManager;
    TaskExecutor taskExecutor;

    public MailSenderServiceImpl(MailSender mailSender,
                                 UserManager userManager, TaskExecutor taskExecutor) {
        this.mailSender = mailSender;
        this.userManager = userManager;
        this.taskExecutor = taskExecutor;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public boolean sendMail(String from, String to, String subject, String msg) {    // sending one mail does not use  new Thread
        try {
            logger.debug("Email sending started to {} with subject : {}", to, subject);
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom(from);
            message.setTo(to);
            message.setSubject(subject);
            message.setText(msg);
            mailSender.send(message);
            logger.debug("Email successfully sent to {} with subject : {}", to, subject);
            return true;

        } catch (Exception e) {
            logger.debug("Email sent Exception : {}", e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    public SimpleMailMessage makeMail(String from, String to, String subject, String msg) {

        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom(from);
        message.setTo(to);
        message.setSubject(subject);
        message.setText(msg);
        return message;
    }

    public void notifyToAll(GNDivision gDiv, User user, String emailSubject, String emailedMessage) {
        logger.debug("------------------------------------->> Status : {}", emailedMessage);
        int dsDivId = gDiv.getDsDivision().getId();
        List<User> users = userManager.getUsersByRoleDS("LO", dsDivId, user);
        ArrayList<SimpleMailMessage> smsList = new ArrayList<SimpleMailMessage>();
        if (users.size() > 0) {
            String emailedMessage1 = emailedMessage + " " + user.getRole().getName() + " : " + user.getFullName();
            for (User u : users) {
                //sendMail("from@no-spam.com", u.getEmail(), emailSubject , emailedMessage1);
                smsList.add(makeMail("from@no-spam.com", u.getEmail(), emailSubject, emailedMessage1));
                logger.debug("------------------------------------->>Email sent to User : {} Email :{}", u.getFullName(), u.getEmail());
            }
            send(smsList);
            logger.debug("Thread........... : {}", "Mail sending given to thread");
        }
    }

    public void notifyToAll(Set<DSDivision> dsDivs, User user, String status) {
        logger.debug("------------------------------------->> Status : {}", status);
        ArrayList<SimpleMailMessage> smsList = new ArrayList<SimpleMailMessage>();
        for (DSDivision dsDiv : dsDivs) {
            List<User> users = userManager.getUsersByRoleDS("LO", dsDiv.getId(), user);

            if (!(users.size() > 0)) {
                continue;
            }
            String message = "The land application " + status + " by " + user.getRole().getName(); // the message might have to be changed
            for (User u : users) {
                // sendMail("from@no-spam.com",u.getEmail(),"Land Application "+status ,message);
                smsList.add(makeMail("from@no-spam.com", u.getEmail(), "Land Application " + status, message));
                logger.debug("------------------------------------->>Email sent to User : {} Email :{}", u.getFullName(), u.getEmail());
            }
        }
        send(smsList);
        logger.debug("Thread........... : {}", "Mail sending given to thread");
    }

    public void notifyToAll(Set<DSDivision> dsDivs, List<String> searchedRoles, User user, String emailSubject, String emailedMessage) {
        ArrayList<SimpleMailMessage> smsList = new ArrayList<SimpleMailMessage>();
        for (DSDivision dsDiv : dsDivs) {
            for (String searchedRole : searchedRoles) {
                List<User> users = userManager.getUsersByRoleDS(searchedRole, dsDiv.getId(), user);
                if (users.size() > 0) {
                    String emailedMessage1 = emailedMessage + " " + user.getRole().getName() + " : " + user.getFullName();
                    for (User u : users) {
                        //sendMail("from@no-spam.com", u.getEmail(), emailSubject , emailedMessage1);
                        smsList.add(makeMail("from@no-spam.com", u.getEmail(), emailSubject, emailedMessage1));
                    }
                }
            }
        }
        send(smsList);
        logger.debug("Thread........... : {}", "Mail sending given to thread");
    }

    public MailSender getMailSender() {
        return mailSender;
    }

    public void setMailSender(MailSender mailSender) {
        this.mailSender = mailSender;
    }


    public void send(ArrayList<SimpleMailMessage> simpleMessage) {     //async task mail sender
        try {
            taskExecutor.execute(new AsyncMailTask(simpleMessage));
        } catch (MailSendException e) {
            e.printStackTrace();
            logger.debug("MailSendException at making new Mail sending Thread : {}",e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            logger.debug("Exception at making new Mail sending Thread: {}",e.getMessage());
        }
    }

    private class AsyncMailTask implements Runnable {

        private ArrayList<SimpleMailMessage> messages;

        private AsyncMailTask(ArrayList<SimpleMailMessage> messages) {
            this.messages = messages;
        }

        public void run() {
            logger.debug("Thread........... : {}", messages.size());
            for (SimpleMailMessage m : messages) {
                logger.debug("Thread...........sending to : {}", m.getTo());
                try {
                    mailSender.send(m);
                } catch (Exception e) {
                    logger.debug("Email sent Exception in Thread : {}", e.getMessage());
                    e.printStackTrace();
                }
            }

        }
    }

}
